package com.yangyang.ralearn.server;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.yangyang.ralearn.common.Constants;
import com.yangyang.ralearn.common.entity.base.AbstractDomainPredicate;
import com.yangyang.ralearn.common.entity.base.BaseAction;
import com.yangyang.ralearn.common.entity.base.BaseActionModelDef;
import com.yangyang.ralearn.common.entity.base.BasePredicate;
import com.yangyang.ralearn.common.entity.domain.RobotAction;
import com.yangyang.ralearn.common.exception.ErrorActionException;
import com.yangyang.ralearn.common.exception.ReachGoalException;
import com.yangyang.ralearn.common.framework.BeanManager;
import com.yangyang.ralearn.common.types.JObject;

/*
 * 模拟器，读取配置文件中的规划，按照客户端给出动作命令执行并返回观察结果
 */
public class JSimulator {

	public List<AbstractDomainPredicate> currentStates;
	public List<AbstractDomainPredicate> goalStates;
	private String mName;
	private int groupId;
	// public static JSimulator Simulator = new
	// JSimulator("simulator--version1.0");
	public List<BaseAction> historyAction;

	private JSimulator(String string, int groupId) {
		// TODO Auto-generated constructor stub
		this.mName = string + groupId;
		this.groupId = groupId;
		currentStates = new ArrayList<AbstractDomainPredicate>();
		goalStates = new ArrayList<AbstractDomainPredicate>();
		this.historyAction = new ArrayList<BaseAction>();
	}

	public void reset() {
		this.currentStates.clear();
		this.goalStates.clear();
	}

	public static JSimulator newInstance(int groupId) {
		return new JSimulator("simulator-version1.0:", groupId);
	}

	public List<AbstractDomainPredicate> observe() {
		int rate = BeanManager.getRLConfig().getObservationPercentage();
		List<AbstractDomainPredicate> list = new ArrayList<>();
		if (rate >= 100)
			list.addAll(this.currentStates);
		else {
			for (AbstractDomainPredicate pre : this.currentStates) {
				if (Math.random() * 100 <= rate) {
					list.add(pre);
				}
			}
		}
		return list;
	}

	public void addInitState(List<String> state) {
		for (String line : state)
			this.currentStates.add(BasePredicate.parsePredicate(ServerManager
					.instance().getEnvironmentById(groupId), line));
	}

	public void addGoalState(List<String> state) {
		for (String line : state)
			this.goalStates.add(BasePredicate.parsePredicate(ServerManager
					.instance().getEnvironmentById(groupId), line));
	}

	public void addObjects(List<String> objects) {
		for (String line : objects) {
			String[] array = line.split("-");
			String type = array[1].trim();
			List<String> data = new ArrayList<String>();
			for (String object : array[0].split(" "))
				data.add(object.trim());
			ServerManager.instance().getEnvironmentById(groupId)
					.addObjectInstance(type, data);
		}

	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder(this.mName);
		sb.append("\ncurrent states\n");
		for (BasePredicate pre : this.currentStates) {
			sb.append(pre + "\n");
		}
		sb.append("goal states");
		for (BasePredicate pre : this.goalStates) {
			sb.append("\n" + pre);
		}
		return sb.toString();
	}

	/**
	 * 判断当前的环境是否能做此动作
	 * 
	 * @param action
	 * @return
	 */
	public boolean canDo(String action) {
		String[] array = action.split("[ ]+");
		JObject[] var = new JObject[array.length - 1];
		for (int i = 1; i < array.length; i++)
			var[i - 1] = ServerManager.instance().getEnvironmentById(groupId).mapName2Object
					.get(array[i]);
		return canDo(new RobotAction(array[0], var));
	}

	public boolean canDo(BaseAction action) {
		BaseActionModelDef def = ServerManager.instance().getEnvironmentById(
				groupId).mapNameActionModelDef.get(action.getType());
		return BaseActionModelDef.canDo(action, def, this.currentStates);

	}

	/**
	 * 执行动作，并更新状态
	 * 
	 * @param action
	 */
	public void doAction(String action) throws ReachGoalException,
			ErrorActionException {
		String[] array = action.split("[ ]+");
		JObject[] var = new JObject[array.length - 1];
		for (int i = 1; i < array.length; i++)
			var[i - 1] = ServerManager.instance().getEnvironmentById(groupId).mapName2Object
					.get(array[i]);
		doAction(new RobotAction(array[0], var));
	}

	public void doAction(BaseAction action) throws ReachGoalException,
			ErrorActionException {
		if (this.currentStates.containsAll(this.goalStates)) {
			Logger.getLogger(JSimulator.class).info(historyAction);
			throw new ReachGoalException("goal reached!");
		}
		if (!canDo(action))
			throw new ErrorActionException("action:" + action
					+ "cann't be performed!");

		BaseActionModelDef def = ServerManager.instance().getEnvironmentById(
				groupId).mapNameActionModelDef.get(action.getType());
		if (def == null) {
			Logger.getLogger(JSimulator.class).error(
					"JSimulator.doaction action :" + action.getType()
							+ " has no default actionmodel");
			assert (false);
		}

		List<AbstractDomainPredicate> predicates = BaseActionModelDef.doAction(
				action, def);
		for (AbstractDomainPredicate pred : predicates) {
			if (pred.getName().charAt(0) == Constants.NEG_PREFIX.charAt(0)) {
				pred.setName(pred.getName().substring(1));
				Logger.getLogger(JSimulator.class).debug(
						this.currentStates.contains(pred));
				this.currentStates.remove(pred);
				Logger.getLogger(JSimulator.class).debug("remove :" + pred);
			} else {
				Logger.getLogger(JSimulator.class).debug(
						this.currentStates.contains(pred));
				this.currentStates.add(pred);
				Logger.getLogger(JSimulator.class).debug("add :" + pred);
			}
			// System.out.println();
		}
		this.historyAction.add(action);
	}
}
